package com.example.academic_affairs_system.config.filter;

import cn.hutool.core.util.StrUtil;
import com.example.academic_affairs_system.common.UserAuthorizationInfo;
import com.example.academic_affairs_system.exception.SystemException;
import com.example.academic_affairs_system.model.entity.AasRole;
import com.example.academic_affairs_system.model.vo.LoginResponseVO;
import com.example.academic_affairs_system.service.IAasRoleService;
import com.example.academic_affairs_system.utils.CommonUtils;
import com.example.academic_affairs_system.utils.TokenUtils;
import com.github.benmanes.caffeine.cache.Cache;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Optional;

/**
 * 权限拦截器
 *
 * @author hjx
 * @since 2023/02/02 12:19 PM
 */
@Component
public class AuthFilter implements Filter {
  @Resource(name = "userAuthorizationCache")
  Cache<UserAuthorizationInfo, Long> userAuthorizationCache;

  @Resource IAasRoleService iAasRoleService;

  @Override
  public void doFilter(
      ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
    HttpServletRequest request = ((HttpServletRequest) servletRequest);
    // 忽略登录、重置密码和登出接口
    if (request.getRequestURI().startsWith("/common/resetPwd")
        || request.getRequestURI().startsWith("/common/login")
        || request.getRequestURI().startsWith("/common/logout")) {
      filterChain.doFilter(servletRequest, servletResponse);
      return;
    }
    // 拦截接口进行权限校验
    String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (StrUtil.isBlank(authorization)) {
      writeError(servletResponse, "请先登陆后重试", null);
    } else {
      LoginResponseVO responseVO = TokenUtils.parseToken(authorization);
      // 测试环境下使用该方式校验，避免频繁登录
      /*      Optional<@NonNull UserAuthorizationInfo> first =
      userAuthorizationCache.asMap().keySet().stream()
          .filter(uai -> uai.getUserId().equals(responseVO.getUserId()))
          .findFirst();*/
      // 正常逻辑下，走以下校验方式
      Optional<@NonNull UserAuthorizationInfo> first =
          userAuthorizationCache.asMap().keySet().stream()
              .filter(uai -> uai.getToken().equals(authorization))
              .findFirst();
      // 不存在
      if (!first.isPresent()) {
        writeError(servletResponse, "无效令牌", "令牌无效请重新登录");
        return;
      }
      // 判断是否过期
      Long expired = userAuthorizationCache.getIfPresent(first.get());
      if (expired != null && expired <= System.currentTimeMillis() / 1000) {
        writeError(servletResponse, "令牌已过期", "请重新登录");
      }
      // 忽略公共接口
      if (request.getRequestURI().startsWith("/common")) {
        filterChain.doFilter(servletRequest, servletResponse);
        return;
      }
      // 接口权限校验
      AasRole roleByUserId;
      try {
        roleByUserId = iAasRoleService.getRoleByUserId(responseVO.getUserId());
      } catch (SystemException e) {
        writeError(servletResponse, e.getMsg(), e.getDesc());
        return;
      }
      if (StrUtil.isNotBlank(roleByUserId.getUrls())) {
        if (roleByUserId.getUrls().equals("*")) {
          // 全部接口
          filterChain.doFilter(servletRequest, servletResponse);
          return;
        } else if (StrUtil.isNotBlank(roleByUserId.getUrls())) {
          // 权限接口校验
          List<String> urls = StrUtil.split(roleByUserId.getUrls(), ",");
          if (!urlContains(urls, request.getRequestURI())) {
            // 不包含
            CommonUtils.writePojo(
                (HttpServletResponse) servletResponse, new SystemException(-999, "没有访问权限", null));
            return;
          }
        }
      } else {
        // 没有指定 urls
        CommonUtils.writePojo(
            (HttpServletResponse) servletResponse,
            new SystemException(-999, "权限校验异常", "请联系负责人查明原因"));
        return;
      }
      filterChain.doFilter(servletRequest, servletResponse);
    }
  }
  /**
   * 判断 urls 中是否包含请求的 url
   *
   * <p>如果 urls 存在 /a/* 之类的数据，那么当 /a/b 、 /a/c 等……一系列接口都视为 true
   *
   * @param urls url 列表
   * @param requestUrl 请求 url
   * @return {@link Boolean} - 是否存在
   */
  private boolean urlContains(List<String> urls, String requestUrl) {
    for (String url : urls) {
      int suffixIndex = url.lastIndexOf("/") + 1;
      String suffixStr = url.substring(suffixIndex);
      String prefix = url.substring(0, suffixIndex),
          requestURIPrefix = requestUrl.substring(0, requestUrl.lastIndexOf("/") + 1);
      if (StrUtil.equals(suffixStr, "*") && prefix.equals(requestURIPrefix)) {
        // 存在任意值且前缀相同
        return true;
      } else if (url.equals(requestUrl)) {
        // 值相同
        return true;
      }
    }
    return false;
  }

  private void writeError(ServletResponse servletResponse, String msg, String desc) {
    CommonUtils.writePojo(
        (HttpServletResponse) servletResponse, new SystemException(-999, msg, desc));
  }
}
